/*
 * Copyright (C) 2015-2018 Alibaba Group Holding Limited
 */

/**
 * 文件用于模拟IPC的功能，仅用于demo测试使用
 * 开发者不需要过多关注，实际产品不会使用到该部分代码
 */

#include "ipc_unit.h"

#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "ulog/ulog.h"
#include "link_visual_struct.h"
#include "cx_utility.h"
#include "dirent.h"
#include "record.h"
#include "cx_hal.h"
#include "cx_record.h"

#define TAG "IPC_UNIT"
IpcUnit::IpcUnit(VideoData video, AudioData audio, PictureData pic, PropertyData property, RecordData record) {
#ifdef DUMMY_IPC
    live_main_ = new MediaParse(videoBufferHandler, audioBufferHandler, this);
    live_sub_ = new MediaParse(videoBufferHandler, audioBufferHandler, this);
    vod_current_ = new MediaParse(videoBufferHandler, audioBufferHandler, this);
    pic_ = new PictureParser(pictureBufferHandler, this);
#else
    live_main_ = new MediaParse(videoBufferHandler, audioBufferHandler, this);
    live_sub_ = new MediaParse(videoBufferHandler, audioBufferHandler, this);
    vod_current_ = new MediaParse(videoBufferHandler, audioBufferHandler, this);
    pic_ = new PictureParser(pictureBufferHandler, this);
#endif
    property_ = new PropertyParser(propertyHandler, this);

    video_data_ = video;
    audio_data_ = audio;
    pic_data_ = pic;
    property_data_ = property;
    record_data_ = record;
#ifdef DUMMY_IPC
    assert(video_data_);
    assert(audio_data_);
    assert(pic_data_);
    assert(property_data_);
#endif
    assert(record_data_);
    start_ = false;

    dev_id_ = -1;
}

IpcUnit::~IpcUnit() {
    delete pic_;
    delete vod_current_;
    delete property_;
    delete live_main_;
    delete live_sub_;
}

int IpcUnit::SetDevAuth(const lv_device_auth_s *auth) {
    if (!(auth && auth->product_key && auth->device_secret && auth->device_name)) {
        return -1;
    }

    auth_pk_ = auth->product_key;
    auth_dn_ = auth->device_name;
    auth_ds_ = auth->device_secret;
    dev_id_ = auth->dev_id;

    return 0;
}

bool IpcUnit::DevAuthCheck(const lv_device_auth_s *auth) {
    if (!auth) {
        return false;
    }

    if (dev_id_ == auth->dev_id &&
        (auth_pk_ == auth->product_key) &&
        (auth_dn_ == auth->device_name) &&
        (auth_ds_ == auth->device_secret)) {
        return true;
    }

    return false;
}

int IpcUnit::Start(const std::vector<std::string> &live,
                   const std::vector<std::string> &vod,
                   const std::string &pic,
                   unsigned int picture_interval_s) {
    if (start_) {
        return 0;
    }
    
#ifdef DUMMY_IPC
    if (live.size() != kMaxNum || vod.size() != kMaxNum) {
        return -1;
    }
    if (!(checkStreamFileGroupExist(live) &&
        checkStreamFileGroupExist(vod) &&
        checkFileExist(pic))) {
        return -1;
    }
#else
    // checkStreamFileGroupExist(vod);
#endif
    /* 直播主子码流和抓图默认打开 */
    live_main_->StartLive(0, live[0]);
    live_sub_->StartLive(0, live[1]);
    // pic_->Start(picture_interval_s, pic);

    /* 点播保存下要播放的文件名 */
    vod_file_ = vod;

    start_ = true;
    return 0;
}

void IpcUnit::Stop() {

}

void IpcUnit::LiveStart(int stream_type, int service_id) {
    int ret;
    if(vod_current_->vod_running_flag) {
        LOGD(TAG, "vod runnning, stop it");
        vod_current_->Stop();
        vod_current_->vod_running_flag = 0;
    }
    if(cx_vr_get_record_status(cx_record_get_handle()))
    {
        LOGD(TAG, "record runnning, stop it");
        cx_vr_record_stop(cx_record_get_handle());
    }
    ret = cx_start_video_encode(1);
    if(ret < 0) {
        return;
    }
    ret = cx_start_audio_encode(1);
    if(ret < 0) {
        return;
    }
    if (stream_type == 0) {
        live_main_->SetServiceId(service_id);
        live_main_->live_running_flag = 1;
    } else {
        live_sub_->SetServiceId(service_id);
        live_sub_->live_running_flag = 1;
    }
}

void IpcUnit::LiveStop(int service_id) {
    if (live_main_->GetServiceId() == service_id) {
        live_main_->SetServiceId(0);
        cx_start_video_encode(0);
        cx_start_audio_encode(0);
        return;
    }

    if (live_sub_->GetServiceId() == service_id) {
        live_sub_->SetServiceId(0);
        cx_start_video_encode(0);
        cx_start_audio_encode(0);
        return;
    }

    LOGE(TAG, "LiveStop failed, service id =%d", service_id);
}

void IpcUnit::LiveRequestIFrame(int service_id) {
    if (live_main_->GetServiceId() == service_id) {
        live_main_->RequestIFrame();
        return;
    }

    if (live_sub_->GetServiceId() == service_id) {
        live_sub_->RequestIFrame();
        return;
    }

    LOGE(TAG, "LiveRequestIFrame failed, service id =%d", service_id);
}

int IpcUnit::LiveGetParam(int service_id, lv_audio_param_s *aparam, lv_video_param_s *vparam) {
    if (!vparam || !aparam) {
        return -1;
    }

    if (live_main_->GetServiceId() == service_id) {
        live_main_->GetParam(*vparam, *aparam);
        return 0;
    }

    if (live_sub_->GetServiceId() == service_id) {
        live_sub_->GetParam(*vparam, *aparam);
        return 0;
    }

    //音频
    if (live_main_->GetServiceId() > 0) {
        live_main_->GetParam(*vparam, *aparam);
        return 0;
    }
    if (live_sub_->GetServiceId() > service_id) {
        live_sub_->GetParam(*vparam, *aparam);
        return 0;
    }

    LOGE(TAG, "LiveGetParam failed, service id =%d", service_id);
    return -1;
}

int IpcUnit::VodGetParam(lv_audio_param_s *aparam, lv_video_param_s *vparam) {
    if (!vparam || !aparam) {
        return -1;
    }

    vod_current_->GetParam(*vparam, *aparam);
    return 0;
}

void IpcUnit::VodStop() {
    vod_current_->Stop();
}

void IpcUnit::VodPause(bool pause) {
    vod_current_->Pause(pause);
}

// static void second_2_hourminsecond(unsigned int second, int *hour, int *min, int *sec)
// {
//     unsigned int second_temp = second;
//     *hour = second_temp / (60*60);
//     second_temp %= (60*60);
//     *min = second_temp / 60;
//     *sec = second_temp % 60; 
// }
extern void *cxrecord_hdl;
#define MAX_RECORD_NUM_PERDAY 100
int IpcUnit::VodSeek(int service_id, unsigned int timestamp_ms) {
    char file_name[128];
    int record_exist = 0;
    struct tm *tm;
    unsigned int relative_daytime_s;
    unsigned int relative_daytime_seg_s;
    int i, j;
    unsigned int index_file_size;
    int ret;
    int vod_segment_count = 0, vod_curent_segment = 0, segment_index_len = 0;
    FILE *index_file;
    if(live_main_->live_running_flag) {
        LOGD(TAG, "live main runnning, stop it");
        live_main_->live_running_flag = 0;
    }
    if(live_sub_->live_running_flag) {
        LOGD(TAG, "live sub runnning, stop it");
        live_sub_->live_running_flag = 0;
    }

    cx_vr_video_info_t info[MAX_RECORD_NUM_PERDAY];
    int vod_record_num = cx_vr_video_search(cxrecord_hdl, check_day_start_time, check_day_start_time + timestamp_ms/1000, info, MAX_RECORD_NUM_PERDAY);

    for(i = 0; i < vod_record_num; i++) {
        time_t time_conver = info[i].begine_time;
        tm = localtime(&time_conver);
        cx_util_utc_to_beijing(tm);
        relative_daytime_s = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
        if((timestamp_ms >= relative_daytime_s*1000) && (timestamp_ms < (relative_daytime_s + info[i].end_time - info[i].begine_time)*1000)) {
            LOGD(TAG, "find record file index %s", info[i].name);
            index_file = fopen(info[i].name, "r");
            if (!index_file) {
                LOGE(TAG, "%s %d Open file failed: %s", __func__, __LINE__, info[i].name);
                return -1;
            }
            cx_record_index_head head_info;
            cx_record_index_segment_head segment_head;
            fseek(index_file, 0, SEEK_END);
            index_file_size = ftell(index_file);
            fseek(index_file, 0, SEEK_SET);
            ret = fread(&head_info, 1, sizeof(head_info), index_file);
            if(ret != sizeof(head_info)) {
                LOGE(TAG, "read head info error");
            }
            LOGD(TAG, "*************record info**************");
            LOGD(TAG, "start time:%llds duration:%dms file_size:%d segment:%d", head_info.start_time, head_info.duration, head_info.file_size, head_info.segment_num);
            LOGD(TAG, "video type:%d fps:%d", head_info.video_param.video_type, head_info.video_param.fps);
            LOGD(TAG, "audio type:%d channel:%d sample_rate:%d sample_bits:%d", head_info.audio_param.audio_type, head_info.audio_param.channel, head_info.audio_param.sample_rate, head_info.audio_param.sample_bits);
            LOGD(TAG, "*************record info**************");
            vod_segment_count = head_info.segment_num;
            for(j=0; j < vod_segment_count; j++) {
                ret = fread(&segment_head, 1, sizeof(segment_head), index_file);
                if(ret != sizeof(segment_head)) {
                    LOGE(TAG, "read segment_head info error");
                }
                // LOGD(TAG, "segment info: %lld %d %d %ld", segment_head.start_time, segment_head.duration, segment_head.next_segment_pos, ftell(index_file));
                time_conver = segment_head.start_time;
                tm = localtime(&time_conver);
                cx_util_utc_to_beijing(tm);
                relative_daytime_seg_s = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
                if((timestamp_ms >= relative_daytime_seg_s*1000) && (timestamp_ms < (relative_daytime_seg_s*1000 + segment_head.duration))) {
                    vod_curent_segment = j + 1;
                    segment_index_len = segment_head.next_segment_pos - ftell(index_file);
                    char *p = strchr(info[i].name, '.');
                    memcpy(file_name, info[i].name, p-info[i].name);
                    file_name[p-info[i].name] = '\0';
                    record_exist = 1;
                    LOGD(TAG, "find record %s:%d", file_name, vod_curent_segment);
                    break;
                }
                fseek(index_file, segment_head.next_segment_pos, SEEK_SET);
            }
            fclose(index_file);
            break;
        }
    }
    
    if(record_exist) {
        // LOGD(TAG, "play record %s seg:%d start_time:%dms", file_name, vod_curent_segment, timestamp_ms - (relative_daytime_s * 1000));
        /* 第一次打开 */
        vod_current_->Pause(true);
        if (!vod_current_->GetStatus()) {
            ret = vod_current_->GetIndexInfo(file_name, vod_segment_count);
            if(ret < 0) {
                return -1;
            }
            vod_current_->PreapareRecordRawData(vod_curent_segment);
            vod_current_->StartVod(service_id, relative_daytime_s * 1000);
        } else {
            LOGD(TAG, "current record: %s:%d last record: %s:%d", file_name, vod_curent_segment, vod_current_->GetStreamPrefix().c_str(), vod_current_->vod_curent_segment);
            if (file_name != vod_current_->GetStreamPrefix()) {
                vod_current_->Stop();  //删除index  index_position置0  线程退出
                ret = vod_current_->GetIndexInfo(file_name, vod_segment_count);
                if(ret < 0) {
                    return -1;
                }
                vod_current_->PreapareRecordRawData(vod_curent_segment);
                vod_current_->StartVod(service_id, relative_daytime_s * 1000);
            } else if(vod_curent_segment != vod_current_->vod_curent_segment) {
                vod_current_->PreapareRecordRawData(vod_curent_segment);
            }
        }
        vod_current_->Seek(timestamp_ms - (relative_daytime_s * 1000));
        vod_current_->Pause(false);
    } else {
        LOGD(TAG, "this time have no record");
    }
    return 0;
}

int IpcUnit::VodSeekFile(int service_id, unsigned int timestamp_ms, const std::string &file) {
    if (file.empty()) {//非第一次打开
        vod_current_->Seek(timestamp_ms);
    } else {//第一次打开
        unsigned int i;
        for (i = 0; i < vod_file_.size(); i ++) {
            if (file == vod_file_[i]) {
                break;
            }
        }
        if (i >= vod_file_.size()) {
            LOGE(TAG, "Query file failed");
            return -1;
        }

        vod_current_->Pause(true);
        vod_current_->StartVod(service_id, 0);
        vod_current_->Seek(0);

    }
    return 0;
}

void IpcUnit::VodSetParam(unsigned int speed, unsigned int key_only) {
    vod_current_->SetSpeed(speed, key_only);
}

void IpcUnit::VodQueryList(const lv_query_record_param_s *param) {
    if (!param) {
        return;
    }
    lv_query_record_response_param_s *response = new lv_query_record_response_param_s;
    memset(response, 0, sizeof(lv_query_record_response_param_s));

    if (param->common.type == LV_QUERY_RECORD_BY_DAY) {
        cx_vr_video_info_t info[MAX_RECORD_NUM_PERDAY];
        time_t utime = param->by_day.start_time;
        struct tm *tm = NULL;
        tm = localtime(&utime);
        if (tm) {
            cx_util_utc_to_beijing(tm);
        }
        check_day_start_time = utime - (tm->tm_hour*3600+tm->tm_min*60+tm->tm_sec);
        int vod_record_num = cx_vr_video_search(cxrecord_hdl, param->by_day.start_time, param->by_day.stop_time, info, MAX_RECORD_NUM_PERDAY);
        LOGD(TAG, "find %d record between %d %d", vod_record_num, param->by_day.start_time, param->by_day.stop_time);
        response->by_day.days = new lv_query_record_response_day[vod_record_num];
        memset(response->by_day.days, 0, sizeof(lv_query_record_response_day) * vod_record_num);
        
        for(int i=0; i < vod_record_num; i++) {
            response->by_day.days[i].file_size = info[i].size;//demo简单赋值
            response->by_day.days[i].record_type = LV_STORAGE_RECORD_INITIATIVE;
            response->by_day.days[i].file_name = new char [strlen(info[i].name)];
            strncpy(response->by_day.days[i].file_name, info[i].name, strlen(info[i].name));
            response->by_day.days[i].start_time = info[i].begine_time;
            response->by_day.days[i].stop_time = info[i].end_time;
        }
        response->by_day.num = vod_record_num;
        record_data_(param->common.service_id, response);
    } else if (param->common.type == LV_QUERY_RECORD_BY_MONTH) {
        response->by_month.months = new int[31];
        memset(response->by_month.months, 0, 31 * sizeof(int));
        /* demo设置部分天数为有录像 */
        response->by_month.months[0] = 1;
        response->by_month.months[10] = 1;
        response->by_month.months[27] = 1;
        record_data_(param->common.service_id, response);
    }

    for (unsigned int i = 0; i < response->by_day.num; i++) {
        delete [] response->by_day.days[i].file_name;
    }
    delete [] response->by_month.months;
    delete [] response->by_day.days;
    delete response;
}

void IpcUnit::SetProperty(const std::string &key, const std::string &value) {
    property_->SetProperty(key, value);
}

void IpcUnit::GetAllProperty() {
    property_->GetAllProperty();
}

int IpcUnit::GetPicture(unsigned char **data, unsigned int *data_len) {
    if (data && data_len) {
        *data = pic_->GetPictureData();
        *data_len = pic_->GetPictureLen();
        return 0;
    }

    return -1;
}

bool IpcUnit::checkStreamFileExist(const std::string &stream_prefix) {
    std::string stream_index = stream_prefix + ".index";
    std::string stream_param = stream_prefix + ".meta";

    return checkFileExist(stream_index) &&
        checkFileExist(stream_param) &&
        checkFileExist(stream_prefix);
}

bool IpcUnit::checkStreamFileGroupExist(const std::vector<std::string> &stream_group) {
    for (std::vector<std::string>::const_iterator search = stream_group.begin(); search != stream_group.end(); ++ search) {
        if (!checkStreamFileExist(*search)) {
            return false;
        }
    }
    return true;
}

bool IpcUnit::checkFileExist(const std::string &file_name) {
    FILE *fp = NULL;
    fp = fopen(file_name.c_str(), "r");
    if (!fp) {
        LOGE(TAG, "Check files failed: %s", file_name.c_str());
        return false;
    }
    fclose(fp);
    return true;
}

void IpcUnit::videoBufferHandler(void *arg, lv_video_format_e format, unsigned char *buffer, unsigned int buffer_size,
                               unsigned int timestamp_ms, int nal_type, int service_id) {
    //LOGD(TAG, "service_id:%d, present_time:%u nal_type:%d ", service_id, timestamp_ms, nal_type);
    if (!arg) {
        return;
    }

    IpcUnit *unit = (IpcUnit *)arg;
    if (service_id > 0) {//service_id大于0再往外抛出数据
        unit->video_data_(service_id, format, buffer, buffer_size, timestamp_ms, nal_type);
    }
}

void IpcUnit::audioBufferHandler(void *arg, lv_audio_format_e format, unsigned char *buffer, unsigned int buffer_size,
                               unsigned int timestamp_ms, int service_id) {
    //LOGD(TAG, "service_id:%d, present_time:%u  buffer_size:%u", service_id, timestamp_ms, buffer_size);
    if (!arg) {
        return;
    }

    IpcUnit *unit = (IpcUnit *)arg;
    if (service_id) {//service_id大于0再往外抛出数据
        unit->audio_data_(service_id, format, buffer, buffer_size, timestamp_ms);
    }
}

void IpcUnit::pictureBufferHandler(void *arg, unsigned char *buffer, unsigned int buffer_len, const char *id) {
    if (!arg) {
        return;
    }

    IpcUnit *unit = (IpcUnit *)arg;
    lv_device_auth_s auth = {0};
    auth.dev_id = unit->dev_id_;
    auth.product_key = (char *)unit->auth_pk_.c_str();
    auth.device_name = (char *)unit->auth_dn_.c_str();
    auth.device_secret = (char *)unit->auth_ds_.c_str();
    unit->pic_data_(&auth, buffer, buffer_len);
}

void IpcUnit::propertyHandler(void *arg, const std::string &value) {
    if (!arg) {
        return;
    }

    IpcUnit *unit = (IpcUnit *)arg;
    lv_device_auth_s auth = {0};
    auth.dev_id = unit->dev_id_;
    auth.product_key = (char *)unit->auth_pk_.c_str();
    auth.device_name = (char *)unit->auth_dn_.c_str();
    auth.device_secret = (char *)unit->auth_ds_.c_str();
    unit->property_data_(&auth, value.c_str());
}
